//! Thanks to https://github.com/nondanee/UnblockNeteaseMusic/blob/master/src/kwDES.js

use neon::prelude::*;

static ARRAY_E: [isize; 64] = [
    31, 0, 1, 2, 3, 4, -1, -1, 3, 4, 5, 6, 7, 8, -1, -1, 7, 8, 9, 10, 11, 12, -1, -1, 11, 12, 13,
    14, 15, 16, -1, -1, 15, 16, 17, 18, 19, 20, -1, -1, 19, 20, 21, 22, 23, 24, -1, -1, 23, 24, 25,
    26, 27, 28, -1, -1, 27, 28, 29, 30, 31, 30, -1, -1,
];

static ARRAY_IP: [isize; 64] = [
    57, 49, 41, 33, 25, 17, 9, 1, 59, 51, 43, 35, 27, 19, 11, 3, 61, 53, 45, 37, 29, 21, 13, 5, 63,
    55, 47, 39, 31, 23, 15, 7, 56, 48, 40, 32, 24, 16, 8, 0, 58, 50, 42, 34, 26, 18, 10, 2, 60, 52,
    44, 36, 28, 20, 12, 4, 62, 54, 46, 38, 30, 22, 14, 6,
];

static ARRAY_IP1: [isize; 64] = [
    39, 7, 47, 15, 55, 23, 63, 31, 38, 6, 46, 14, 54, 22, 62, 30, 37, 5, 45, 13, 53, 21, 61, 29,
    36, 4, 44, 12, 52, 20, 60, 28, 35, 3, 43, 11, 51, 19, 59, 27, 34, 2, 42, 10, 50, 18, 58, 26,
    33, 1, 41, 9, 49, 17, 57, 25, 32, 0, 40, 8, 48, 16, 56, 24,
];

static ARRAY_LS: [usize; 16] = [1, 1, 2, 2, 2, 2, 2, 2, 1, 2, 2, 2, 2, 2, 2, 1];

static ARRAY_LS_MASK: [i64; 3] = [0, 0x100001, 0x300003];

static ARRAY_MASK: [i64; 64] = [
    1,
    2,
    4,
    8,
    16,
    32,
    64,
    128,
    256,
    512,
    1024,
    2048,
    4096,
    8192,
    16384,
    32768,
    65536,
    131072,
    262144,
    524288,
    1048576,
    2097152,
    4194304,
    8388608,
    16777216,
    33554432,
    67108864,
    134217728,
    268435456,
    536870912,
    1073741824,
    2147483648,
    4294967296,
    8589934592,
    17179869184,
    34359738368,
    68719476736,
    137438953472,
    274877906944,
    549755813888,
    1099511627776,
    2199023255552,
    4398046511104,
    8796093022208,
    17592186044416,
    35184372088832,
    70368744177664,
    140737488355328,
    281474976710656,
    562949953421312,
    1125899906842624,
    2251799813685248,
    4503599627370496,
    9007199254740992,
    18014398509481984,
    36028797018963968,
    72057594037927936,
    144115188075855872,
    288230376151711744,
    576460752303423488,
    1152921504606846976,
    2305843009213693952,
    4611686018427387904,
    -9223372036854775808,
];

static ARRAY_P: [isize; 32] = [
    15, 6, 19, 20, 28, 11, 27, 16, 0, 14, 22, 25, 4, 17, 30, 9, 1, 7, 23, 13, 31, 26, 2, 8, 18, 12,
    29, 5, 21, 10, 3, 24,
];

static ARRAY_PC1: [isize; 56] = [
    56, 48, 40, 32, 24, 16, 8, 0, 57, 49, 41, 33, 25, 17, 9, 1, 58, 50, 42, 34, 26, 18, 10, 2, 59,
    51, 43, 35, 62, 54, 46, 38, 30, 22, 14, 6, 61, 53, 45, 37, 29, 21, 13, 5, 60, 52, 44, 36, 28,
    20, 12, 4, 27, 19, 11, 3,
];

static ARRAY_PC2: [isize; 64] = [
    13, 16, 10, 23, 0, 4, -1, -1, 2, 27, 14, 5, 20, 9, -1, -1, 22, 18, 11, 3, 25, 7, -1, -1, 15, 6,
    26, 19, 12, 1, -1, -1, 40, 51, 30, 36, 46, 54, -1, -1, 29, 39, 50, 44, 32, 47, -1, -1, 43, 48,
    38, 55, 33, 52, -1, -1, 45, 41, 49, 35, 28, 31, -1, -1,
];

static MATRIX_NS_BOX: [[i64; 64]; 8] = [
    [
        14, 4, 3, 15, 2, 13, 5, 3, 13, 14, 6, 9, 11, 2, 0, 5, 4, 1, 10, 12, 15, 6, 9, 10, 1, 8, 12,
        7, 8, 11, 7, 0, 0, 15, 10, 5, 14, 4, 9, 10, 7, 8, 12, 3, 13, 1, 3, 6, 15, 12, 6, 11, 2, 9,
        5, 0, 4, 2, 11, 14, 1, 7, 8, 13,
    ],
    [
        15, 0, 9, 5, 6, 10, 12, 9, 8, 7, 2, 12, 3, 13, 5, 2, 1, 14, 7, 8, 11, 4, 0, 3, 14, 11, 13,
        6, 4, 1, 10, 15, 3, 13, 12, 11, 15, 3, 6, 0, 4, 10, 1, 7, 8, 4, 11, 14, 13, 8, 0, 6, 2, 15,
        9, 5, 7, 1, 10, 12, 14, 2, 5, 9,
    ],
    [
        10, 13, 1, 11, 6, 8, 11, 5, 9, 4, 12, 2, 15, 3, 2, 14, 0, 6, 13, 1, 3, 15, 4, 10, 14, 9, 7,
        12, 5, 0, 8, 7, 13, 1, 2, 4, 3, 6, 12, 11, 0, 13, 5, 14, 6, 8, 15, 2, 7, 10, 8, 15, 4, 9,
        11, 5, 9, 0, 14, 3, 10, 7, 1, 12,
    ],
    [
        7, 10, 1, 15, 0, 12, 11, 5, 14, 9, 8, 3, 9, 7, 4, 8, 13, 6, 2, 1, 6, 11, 12, 2, 3, 0, 5,
        14, 10, 13, 15, 4, 13, 3, 4, 9, 6, 10, 1, 12, 11, 0, 2, 5, 0, 13, 14, 2, 8, 15, 7, 4, 15,
        1, 10, 7, 5, 6, 12, 11, 3, 8, 9, 14,
    ],
    [
        2, 4, 8, 15, 7, 10, 13, 6, 4, 1, 3, 12, 11, 7, 14, 0, 12, 2, 5, 9, 10, 13, 0, 3, 1, 11, 15,
        5, 6, 8, 9, 14, 14, 11, 5, 6, 4, 1, 3, 10, 2, 12, 15, 0, 13, 2, 8, 5, 11, 8, 0, 15, 7, 14,
        9, 4, 12, 7, 10, 9, 1, 13, 6, 3,
    ],
    [
        12, 9, 0, 7, 9, 2, 14, 1, 10, 15, 3, 4, 6, 12, 5, 11, 1, 14, 13, 0, 2, 8, 7, 13, 15, 5, 4,
        10, 8, 3, 11, 6, 10, 4, 6, 11, 7, 9, 0, 6, 4, 2, 13, 1, 9, 15, 3, 8, 15, 3, 1, 14, 12, 5,
        11, 0, 2, 12, 14, 7, 5, 10, 8, 13,
    ],
    [
        4, 1, 3, 10, 15, 12, 5, 0, 2, 11, 9, 6, 8, 7, 6, 9, 11, 4, 12, 15, 0, 3, 10, 5, 14, 13, 7,
        8, 13, 14, 1, 2, 13, 6, 14, 9, 4, 1, 2, 14, 11, 13, 5, 0, 1, 10, 8, 3, 0, 11, 3, 5, 9, 4,
        15, 2, 7, 8, 12, 15, 10, 7, 6, 12,
    ],
    [
        13, 7, 10, 0, 6, 9, 5, 15, 8, 4, 3, 10, 11, 14, 12, 5, 2, 11, 9, 6, 15, 12, 0, 3, 4, 1, 14,
        13, 1, 2, 7, 8, 1, 2, 12, 15, 10, 4, 0, 3, 13, 14, 6, 9, 7, 8, 9, 6, 15, 1, 5, 12, 3, 10,
        14, 5, 8, 7, 11, 0, 4, 13, 2, 11,
    ],
];

fn bit_transform(arr: &[isize], n: usize, l: i64) -> i64 {
    let mut l2 = 0i64;
    for i in 0..n {
        if arr[i] >= 0 && (l & (ARRAY_MASK[arr[i] as usize])) != 0 {
            l2 |= ARRAY_MASK[i];
        }
    }
    l2
}

fn des64(longs: &[i64], l: i64) -> i64 {
    let mut p_r: [usize; 8] = [0, 0, 0, 0, 0, 0, 0, 0];
    let mut out = bit_transform(&ARRAY_IP, 64, l);
    let mut p_source = [out & 0xffffffff, (out & -4294967296) >> 32];

    for i in 0..16 {
        let r = bit_transform(&ARRAY_E, 64, p_source[1]) ^ longs[i];
        for j in 0..8 {
            p_r[j] = ((r >> (j * 8)) & 255) as usize;
        }

        let mut s_out = 0;
        for sbi in (0..8).rev() {
            s_out = (s_out << 4) | MATRIX_NS_BOX[sbi][p_r[sbi]];
        }

        let l = p_source[0];
        p_source[0] = p_source[1];
        p_source[1] = l ^ bit_transform(&ARRAY_P, 32, s_out);
    }

    out = ((p_source[0] << 32) & -4294967296) | (p_source[1] & 0xffffffff);
    bit_transform(&ARRAY_IP1, 64, out)
}

// static KEY: [i64; 8] = [121, 108, 122, 115, 120, 107, 119, 108];
static L: i64 = 7815833843402435705;

pub fn kuwo_crypt(mut cx: FunctionContext) -> JsResult<JsBuffer> {
    let msg = cx.argument::<JsString>(0)?.value(&mut cx);
    let msg = msg.as_bytes();
    let m = msg.len() / 8;

    /* let mut l = 0i64;
    for i in 0..8 {
        l |= KEY[i] << (i * 8);
    } */

    let mut arr1: [i64; 16] = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0];
    let mut l = bit_transform(&ARRAY_PC1, 56, L);
    for i in 0..16 {
        l = ((l & (ARRAY_LS_MASK[ARRAY_LS[i]])) << (28 - ARRAY_LS[i]))
            | ((l & (!ARRAY_LS_MASK[ARRAY_LS[i]])) >> (ARRAY_LS[i]));
        arr1[i] = bit_transform(&ARRAY_PC2, 64, l);
    }

    let mut arr2: Vec<i64> = Vec::with_capacity(m);
    for i in 0..m {
        arr2.push(0);
        for j in 0..8 {
            arr2[i] |= (msg[i * 8 + j] as i64) << (j * 8);
        }
    }

    let mut arr3: Vec<i64> = Vec::with_capacity(m + 1);
    for i in arr2 {
        arr3.push(des64(&arr1, i));
    }

    let mut l: i64 = 0;
    for i in 0..(msg.len() % 8) {
        l |= (msg[m * 8 + i] as i64) << (i * 8);
    }
    arr3.push(des64(&arr1, l));

    let mut bytes: Vec<u8> = Vec::with_capacity(m + 8);
    for i in arr3 {
        for j in 0..8 {
            bytes.push(((i >> (j * 8)) & 255) as u8);
        }
    }

    let mut buf = cx.buffer(bytes.len() as u32)?;
    cx.borrow_mut(&mut buf, |buf| {
        let buf = buf.as_mut_slice();
        buf.copy_from_slice(&bytes);
    });

    Ok(buf)
}
